package com.xunlai.infra.permission.business.service.impl;

import com.xunlai.infra.permission.business.converter.PosterConverter;
import com.xunlai.infra.permission.business.converter.RouteConverter;
import com.xunlai.infra.permission.business.dao.entity.*;
import com.xunlai.infra.permission.business.dao.entity.vo.RouteDetailInfoVo;
import com.xunlai.infra.permission.business.dao.entity.vo.RouteTemplateCountVo;
import com.xunlai.infra.permission.business.dao.repository.*;
import com.xunlai.infra.permission.business.dao.repository.vo.RouteDetailInfoVoRepository;
import com.xunlai.infra.permission.business.service.RouteService;
import com.xunlai.infra.permission.business.vo.poster.PosterDetailVo;
import com.xunlai.infra.permission.business.vo.route.*;
import com.xunlai.infra.permission.common.exception.DataNotExistException;
import com.xunlai.infra.permission.common.exception.PermissionBusinessException;
import com.xunlai.infra.permission.common.vo.BasePageRespVo;
import com.xunlai.infra.permission.storage.entity.UserAccount;
import com.xunlai.infra.permission.storage.repository.UserAccountRepository;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * @author liang
 * @date 8/2/23 5:48 PM
 */
@Service
public class RouteServiceImpl implements RouteService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RouteServiceImpl.class);

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private RouteInfoRepository routeInfoRepository;

    private RouteIntroductionRepository routeIntroductionRepository;

    private RouteScheduleRepository routeScheduleRepository;

    private RouteExtraInfoRepository routeExtraInfoRepository;

    private RouteBindPosterRepository routeBindPosterRepository;

    private RouteTypeRepository routeTypeRepository;

    private RouteSubjectRepository routeSubjectRepository;

    private RouteDetailInfoVoRepository routeDetailInfoVoRepository;

    private RouteTemplateRepository routeTemplateRepository;

    private RouteContactRepository routeContactRepository;

    private UserAccountRepository userAccountRepository;

    private RouteStatisticsRepository routeStatisticsRepository;

    private PosterRepository posterRepository;

    private RefContactRouteRepository refContactRouteRepository;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long createRoute(RouteCreateReqVo reqVo) {
        RouteInfo routeInfo = RouteConverter.INSTANCE.convertCreateVo2RouteEntity(reqVo);
        Optional<RouteTemplate> routeTemplateOptional = routeTemplateRepository.findById(routeInfo.getRouteTemplate());
        routeTemplateOptional.ifPresent(routeTemplate -> routeInfo.setTemplateName(routeTemplate.getTemplateName()));
        Optional<RouteType> routeTypeOptional = routeTypeRepository.findById(routeInfo.getRouteType());
        routeTypeOptional.ifPresent(routeType -> routeInfo.setTypeName(routeType.getTypeName()));
        Optional<RouteSubject> routeSubjectOptional = routeSubjectRepository.findById(routeInfo.getRouteSubject());
        routeSubjectOptional.ifPresent(routeSubject -> routeInfo.setSubjectName(routeSubject.getSubjectName()));
        routeInfo.setRouteNo(String.valueOf(System.currentTimeMillis()));
        RouteInfo info = routeInfoRepository.save(routeInfo);
        RouteIntroduction routeIntroduction = RouteConverter.INSTANCE.convertCreateVo2IntroductionEntity(reqVo);
        Long routeId = info.getId();
        routeIntroduction.setRouteId(routeId);
        routeIntroductionRepository.save(routeIntroduction);
        RouteExtraIntro routeExtraIntro = RouteConverter.INSTANCE.convertCreateVo2ExtraIntroEntity(reqVo);
        routeExtraIntro.setRouteId(routeId);
        routeExtraInfoRepository.save(routeExtraIntro);
        List<RouteScheduleVo> routeSchedules = reqVo.getRouteSchedules();
        List<RouteSchedule> scheduleList = routeSchedules.stream().map(rs -> {
            RouteSchedule routeSchedule = RouteConverter.INSTANCE.convertCreateVo2ScheduleEntity(rs);
            routeSchedule.setRouteId(routeId);
            return routeSchedule;
        }).toList();
        routeScheduleRepository.saveAll(scheduleList);
        return routeId;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updateRoute(Long routeId,RouteCreateReqVo routeCreateReqVo) {
        Optional<RouteInfo> routeInfoOptional = routeInfoRepository.findById(routeId);
        if(routeInfoOptional.isPresent()){
            RouteInfo existInfo = routeInfoOptional.get();
            BeanUtils.copyProperties(routeCreateReqVo,existInfo);
            routeInfoRepository.save(existInfo);
        }else {
            RouteInfo routeInfo = RouteConverter.INSTANCE.convertCreateVo2RouteEntity(routeCreateReqVo);
            RouteInfo save = routeInfoRepository.save(routeInfo);
            routeId = save.getId();
        }
        RouteIntroduction routeIntroduction = RouteConverter.INSTANCE.convertCreateVo2IntroductionEntity(routeCreateReqVo);
        routeIntroduction.setRouteId(routeId);
        routeIntroductionRepository.deleteByRouteId(routeId);
        routeIntroductionRepository.save(routeIntroduction);
        RouteExtraIntro routeExtraIntro = RouteConverter.INSTANCE.convertCreateVo2ExtraIntroEntity(routeCreateReqVo);
        routeExtraIntro.setRouteId(routeId);
        routeExtraInfoRepository.deleteByRouteId(routeId);
        routeExtraInfoRepository.save(routeExtraIntro);
        List<RouteScheduleVo> routeSchedules = routeCreateReqVo.getRouteSchedules();
        final Long rId = routeId;
        List<RouteSchedule> scheduleList = routeSchedules.stream().map(rs -> {
            RouteSchedule routeSchedule = RouteConverter.INSTANCE.convertCreateVo2ScheduleEntity(rs);
            routeSchedule.setRouteId(rId);
            return routeSchedule;
        }).toList();
        routeScheduleRepository.deleteAllByRouteId(routeId);
        routeScheduleRepository.saveAll(scheduleList);
        return true;
    }

    @Override
    public RouteDetailVo getRouteDetail(Long id) {
        Optional<RouteDetailInfoVo> detailInfoVoOptional = routeDetailInfoVoRepository.findById(id);
        if(detailInfoVoOptional.isEmpty()) {
            return null;
        }
        RouteDetailInfoVo routeDetailInfoVo = detailInfoVoOptional.get();
        List<RouteSchedule> routeScheduleList = routeDetailInfoVo.getRouteScheduleList();
        RouteIntroduction routeIntroduction = routeDetailInfoVo.getRouteIntroduction();
        RouteExtraIntro routeExtraIntro = routeDetailInfoVo.getRouteExtraIntro();
        RouteDetailVo routeDetailVo = new RouteDetailVo();
        try {
            BeanUtils.copyProperties(routeDetailInfoVo,routeDetailVo);
            BeanUtils.copyProperties(routeIntroduction,routeDetailVo);
            BeanUtils.copyProperties(routeExtraIntro,routeDetailVo);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(),e);
        }
        List<RouteScheduleVo> routeScheduleVos = routeScheduleList.stream().map(RouteConverter.INSTANCE::convertScheduleEntity2Vo).toList();
        routeDetailVo.setRouteSchedules(routeScheduleVos);
        return routeDetailVo;
    }

    @Override
    public RouteContactDetailVo getRouteContactDetail(Long routeId, Long contactId,String posterNo) {
        if(routeId == null){
            return null;
        }
        RouteDetailVo routeDetail = getRouteDetail(routeId);
        if(routeDetail == null){
            return null;
        }
        final RouteContactDetailVo routeContactDetailVo = new RouteContactDetailVo();
        routeContactDetailVo.setRouteInfo(routeDetail);
        Optional<RouteContact> contactOptional;
        if(contactId == null || (contactOptional = routeContactRepository.findById(contactId)).isEmpty()){
            Long createUser = routeDetail.getCreateUser();
//            if(createUser == null){
//                return routeContactDetailVo;
//            }
//            Optional<UserAccount> accountOptional = userAccountRepository.findById(createUser);
            RouteContactVo routeContactVo = new RouteContactVo();
            routeContactVo.setRouteId(routeId);
            routeContactVo.setContactUserId(createUser);
            routeContactVo.setContactUserMobile(routeDetail.getRouteDesignerMobile());
            routeContactVo.setContactUserName(routeDetail.getRouteDesignerName());
            routeContactVo.setContactUserWxUri(routeDetail.getRouteDesignerWxUri());
//            accountOptional.ifPresent(userAccount -> {
//            });
            if(StringUtils.isBlank(posterNo)){
                Optional<RouteBindPoster> bindPosterOptional = routeBindPosterRepository.findByRouteIdAndIdType(routeId,"ROUTE");
                if(bindPosterOptional.isPresent()){
                    RouteBindPoster routeBindPoster = bindPosterOptional.get();
                    Long posterId = routeBindPoster.getPosterId();
                    routeContactVo.setPosterId(posterId);
                    Optional<Poster> posterOptional = posterRepository.findById(posterId);
                    posterOptional.ifPresent(poster -> {
                        PosterDetailVo posterDetailVo = PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
                        routeContactDetailVo.setPosterInfo(posterDetailVo);
                    });
                }
            }else {
                Optional<Poster> posterOptional = posterRepository.findByPosterNo(posterNo);
                posterOptional.ifPresent(poster -> {
                    PosterDetailVo posterDetailVo = PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
                    routeContactDetailVo.setPosterInfo(posterDetailVo);
                });
            }

            routeContactDetailVo.setContactInfo(routeContactVo);
        }else {
            RouteContact routeContact = contactOptional.get();
            RouteContactVo routeContactVo = RouteConverter.INSTANCE.convertContactEntity2Vo(routeContact);
            routeContactDetailVo.setContactInfo(routeContactVo);
            if(StringUtils.isBlank(posterNo)){
                Long posterId = routeContact.getPosterId();
                if(posterId != null){
                    Optional<Poster> posterOptional = posterRepository.findById(posterId);
                    posterOptional.ifPresent(poster -> {
                        PosterDetailVo posterDetailVo = PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
                        routeContactDetailVo.setPosterInfo(posterDetailVo);
                    });
                }else {
                    Optional<RouteBindPoster> bindPosterOptional = routeBindPosterRepository.findByRouteIdAndIdType(routeId,"ROUTE");
                    if(bindPosterOptional.isPresent()){
                        RouteBindPoster routeBindPoster = bindPosterOptional.get();
                        Optional<Poster> posterOptional = posterRepository.findById(routeBindPoster.getPosterId());
                        posterOptional.ifPresent(poster -> {
                            PosterDetailVo posterDetailVo = PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
                            routeContactDetailVo.setPosterInfo(posterDetailVo);
                        });
                    }
                }
            }else {
                Optional<Poster> posterOptional = posterRepository.findByPosterNo(posterNo);
                posterOptional.ifPresent(poster -> {
                    PosterDetailVo posterDetailVo = PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
                    routeContactDetailVo.setPosterInfo(posterDetailVo);
                });
            }

        }
        return routeContactDetailVo;

    }

    @Deprecated
    @Override
    public BasePageRespVo<RouteListVo> listRoute(RouteQueryVo routeQueryVo, Integer pageNum, Integer pageSize) {
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        if(routeQueryVo == null){
            Page<RouteInfo> routeInfoPage = routeInfoRepository.findAll(PageRequest.of(pageNum - 1, size));
            List<RouteListVo> routeListVos = routeInfoPage.getContent().stream().map(RouteConverter.INSTANCE::convertInfoEntity2ListVo).toList();
            return new BasePageRespVo<>(routeListVos,pageNum,size,routeInfoPage.getTotalElements(), routeInfoPage.getTotalPages());
        }
        if(routeQueryVo.getId() != null){
            Optional<RouteInfo> routeInfoOptional = routeInfoRepository.findById(routeQueryVo.getId());
            if(routeInfoOptional.isEmpty()){
                return new BasePageRespVo<>(new ArrayList<>(0),pageNum,pageSize,0L,1);
            }else {
                return new BasePageRespVo<>(Collections.singletonList(RouteConverter.INSTANCE.convertInfoEntity2ListVo(routeInfoOptional.get())),pageNum,pageSize,1L,1);
            }
        }
        String routeDestination = routeQueryVo.getRouteDestination();
        Long routeType = routeQueryVo.getRouteType();
        Long routeTemplate = routeQueryVo.getRouteTemplate();
//        List<LocalDateTime> createTimeRange = routeQueryVo.getCreateTimeRange();
        String[] createTimeRange = routeQueryVo.getCreateTimeRange();
        Long routeSubject = routeQueryVo.getRouteSubject();
        String routeNo = routeQueryVo.getRouteNo();
        if(createTimeRange == null || createTimeRange.length == 0){
            RouteInfo routeInfo = RouteConverter.INSTANCE.convertQueryVo2InfoEntity(routeQueryVo);

            Page<RouteInfo> routeInfoPage = routeInfoRepository.findAll(Example.of(routeInfo, ExampleMatcher.matching().withIgnoreNullValues().withMatcher("routeDestination", ExampleMatcher.GenericPropertyMatcher::contains)), PageRequest.of(pageNum - 1, size));
            List<RouteListVo> routeListVos = routeInfoPage.getContent().stream().map(RouteConverter.INSTANCE::convertInfoEntity2ListVo).toList();
            return new BasePageRespVo<>(routeListVos,pageNum,size,routeInfoPage.getTotalElements(), routeInfoPage.getTotalPages());
        }
        LocalDateTime start = LocalDateTime.from(dateTimeFormatter.parse(createTimeRange[0]));
        LocalDateTime end ;
        if(createTimeRange.length == 1 || createTimeRange[1] == null){
            end = LocalDateTime.now();
        }else {
            end = LocalDateTime.from(dateTimeFormatter.parse(createTimeRange[1]));
        }
        Long total = routeInfoRepository.countByCreateTimeBetween(start, end, routeType, routeSubject, routeTemplate,routeNo, routeDestination);
        if(total > 0L){
            List<RouteInfo> routeInfos = routeInfoRepository.selectAllByCreateTimeBetween(start, end, routeType, routeSubject, routeTemplate,routeNo, routeDestination, pageNum - 1, pageSize);
            List<RouteListVo> routeListVos = routeInfos.stream().map(RouteConverter.INSTANCE::convertInfoEntity2ListVo).toList();
            Long page = (total + size -1) /size;// total % size == 0 ? total / size : (total / size) + 1;
            return new BasePageRespVo<>(routeListVos,pageNum,size,total, page.intValue());
        }else {
            return new BasePageRespVo<>(new ArrayList<>(0),pageNum,pageSize,0L,1);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public BasePageRespVo<RouteListVo> listRoute(Long userId,RouteQueryVo routeQueryVo, Integer pageNum, Integer pageSize) {
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        if(routeQueryVo == null){
            RouteInfo query = new RouteInfo();
            query.setCreateUser(userId);
            Example<RouteInfo> example = Example.of(query, ExampleMatcher.matching().withIgnoreNullValues());
            Page<RouteInfo> routeInfoPage = routeInfoRepository.findAll(example,PageRequest.of(pageNum - 1, size));
            List<RouteListVo> routeListVos = routeInfoPage.getContent().stream().map(RouteConverter.INSTANCE::convertInfoEntity2ListVo).toList();
            return new BasePageRespVo<>(routeListVos,pageNum,size,routeInfoPage.getTotalElements(), routeInfoPage.getTotalPages());
        }
        if(routeQueryVo.getId() != null){
            Optional<RouteInfo> routeInfoOptional = userId == null ? routeInfoRepository.findById(routeQueryVo.getId()) : routeInfoRepository.findByIdAndCreateUser(routeQueryVo.getId(),userId);
            if(routeInfoOptional.isEmpty()){
                return new BasePageRespVo<>(new ArrayList<>(0),pageNum,pageSize,0L,1);
            }else {
                return new BasePageRespVo<>(Collections.singletonList(RouteConverter.INSTANCE.convertInfoEntity2ListVo(routeInfoOptional.get())),pageNum,pageSize,1L,1);
            }
        }
        String routeDestination = routeQueryVo.getRouteDestination();
        Long routeType = routeQueryVo.getRouteType();
        Long routeTemplate = routeQueryVo.getRouteTemplate();
//        List<LocalDateTime> createTimeRange = routeQueryVo.getCreateTimeRange();
        String[] createTimeRange = routeQueryVo.getCreateTimeRange();
        Long routeSubject = routeQueryVo.getRouteSubject();
        String routeNo = routeQueryVo.getRouteNo();
        if(createTimeRange == null || createTimeRange.length == 0){
            RouteInfo routeInfo = RouteConverter.INSTANCE.convertQueryVo2InfoEntity(routeQueryVo);
            routeInfo.setCreateUser(userId);
            Page<RouteInfo> routeInfoPage = routeInfoRepository.findAll(Example.of(routeInfo, ExampleMatcher.matching().withIgnoreNullValues().withMatcher("routeDestination", ExampleMatcher.GenericPropertyMatcher::contains)), PageRequest.of(pageNum - 1, size));
            List<RouteListVo> routeListVos = routeInfoPage.getContent().stream().map(RouteConverter.INSTANCE::convertInfoEntity2ListVo).toList();
            return new BasePageRespVo<>(routeListVos,pageNum,size,routeInfoPage.getTotalElements(), routeInfoPage.getTotalPages());
        }

        LocalDateTime start = LocalDateTime.from(dateTimeFormatter.parse(createTimeRange[0]));
        LocalDateTime end ;
        if(createTimeRange.length == 1 || createTimeRange[1] == null){
            end = LocalDateTime.now();
        }else {
            end = LocalDateTime.from(dateTimeFormatter.parse(createTimeRange[1]));
        }
        Long total = routeInfoRepository.countByCreateTimeBetween(start, end, routeType, routeSubject, routeTemplate,routeNo, routeDestination,userId);
        if(total > 0L){
            List<RouteInfo> routeInfos = routeInfoRepository.selectAllByCreateTimeBetween(start, end, routeType, routeSubject, routeTemplate,routeNo, routeDestination, userId,pageNum - 1, size);
            List<RouteListVo> routeListVos = routeInfos.stream().map(RouteConverter.INSTANCE::convertInfoEntity2ListVo).toList();
            Long page = (total + size -1) /size;// total % size == 0 ? total / size : (total / size) + 1;
            return new BasePageRespVo<>(routeListVos,pageNum,size,total, page.intValue());
        }else {
            return new BasePageRespVo<>(new ArrayList<>(0),pageNum,pageSize,0L,1);
        }
    }

    @Override
    public Boolean onlineRoute(Long id) throws PermissionBusinessException {
        Optional<RouteInfo> routeInfoOptional = routeInfoRepository.findById(id);
        if(routeInfoOptional.isEmpty()){
            throw new DataNotExistException();
        }
        RouteInfo routeInfo = routeInfoOptional.get();
        routeInfo.setValid(true);
        routeInfoRepository.save(routeInfo);
        return true;
    }

    @Override
    public Boolean offlineRoute(Long id) throws PermissionBusinessException {
        Optional<RouteInfo> routeInfoOptional = routeInfoRepository.findById(id);
        if(routeInfoOptional.isEmpty()){
            throw new DataNotExistException();
        }
        RouteInfo routeInfo = routeInfoOptional.get();
        routeInfo.setValid(false);
        routeInfoRepository.save(routeInfo);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteRoute(Long id) throws PermissionBusinessException {
        routeIntroductionRepository.deleteByRouteId(id);
        routeExtraInfoRepository.deleteByRouteId(id);
        routeScheduleRepository.deleteAllByRouteId(id);
        routeInfoRepository.deleteById(id);
        return true;
    }

    @Override
    public void deleteRoute(RouteBatchDeleteVo routeBatchDeleteVo) throws PermissionBusinessException {
        List<Long> ids = routeBatchDeleteVo.getIds();
        if(CollectionUtils.isEmpty(ids)){
            return;
        }
        routeIntroductionRepository.deleteAllByRouteIdIn(ids);
        routeExtraInfoRepository.deleteAllByRouteIdIn(ids);
        routeScheduleRepository.deleteAllByRouteIdIn(ids);
        routeInfoRepository.deleteAllById(ids);
    }

    @Override
    public void bindPosterTemplate(RouteBindPosterVo routeBindPosterVo) {
        Long routeId = routeBindPosterVo.getRouteId();
        if(routeId == null){
            return;
        }
        Optional<RouteBindPoster> bindPosterOptional = routeBindPosterRepository.findByRouteIdAndIdType(routeId,"ROUTE");
        if(bindPosterOptional.isEmpty() ){
            RouteBindPoster routeBindPoster = RouteConverter.INSTANCE.convertBindVo2Entity(routeBindPosterVo);
            routeBindPoster.setIdType("ROUTE");
            routeBindPosterRepository.save(routeBindPoster);
        }else {
            RouteBindPoster routeBindPoster = bindPosterOptional.get();
            if(!Objects.equals(routeBindPoster.getPosterId(),routeBindPosterVo.getPosterId())){
                routeBindPoster.setPosterId(routeBindPosterVo.getPosterId());
                routeBindPosterRepository.save(routeBindPoster);
            }
        }
    }

    @Override
    public Long createRouteType(RouteTypeCreateVo routeTypeCreateVo) {
        RouteType routeType = RouteConverter.INSTANCE.convertCreateVo2RouteTypeEntity(routeTypeCreateVo);
        RouteType save = routeTypeRepository.save(routeType);
        return save.getId();
    }

    @Override
    public BasePageRespVo<RouteTypeListVo> listRouteType(RouteTypeQueryVo routeTypeQueryVo, Integer pageNum, Integer pageSize) {
        RouteType routeType = RouteConverter.INSTANCE.convertQueryVo2RouteTypeEntity(routeTypeQueryVo);
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        Page<RouteType> routeTypePage = routeTypeRepository.findAll(Example.of(routeType, ExampleMatcher.matching().withIgnoreNullValues().withMatcher("typeName", ExampleMatcher.GenericPropertyMatcher::contains)), PageRequest.of(pageNum - 1, size));
        List<RouteTypeListVo> routeTypeListVos = routeTypePage.getContent().stream().map(RouteConverter.INSTANCE::convertRouteTypeEntity2ListVo).toList();
        return new BasePageRespVo<>(routeTypeListVos,pageNum,size,routeTypePage.getTotalElements(), routeTypePage.getTotalPages());

    }

    @Override
    public List<RouteTypeListVo> listAllRouteType() {
        Iterable<RouteType> all = routeTypeRepository.findAll();
        return StreamSupport.stream(all.spliterator(), false).map(RouteConverter.INSTANCE::convertRouteTypeEntity2ListVo).toList();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateRouteType(Long id, RouteTypeCreateVo routeTypeCreateVo) {
        String typeName = routeTypeCreateVo.getTypeName();
        if(StringUtils.isBlank(typeName)){
            return;
        }
        Optional<RouteType> routeTypeOptional = routeTypeRepository.findById(id);
        if(routeTypeOptional.isEmpty()){
            return;
        }
        RouteType routeType = routeTypeOptional.get();
        routeType.setTypeName(typeName);
        routeTypeRepository.save(routeType);
    }

    @Override
    public Long createRouteSubject(RouteSubjectCreateVo routeSubjectCreateVo) {
        RouteSubject routeSubject = RouteConverter.INSTANCE.convertCreateVo2RouteSubjectEntity(routeSubjectCreateVo);
        RouteSubject save = routeSubjectRepository.save(routeSubject);
        return save.getId();
    }

    @Override
    public void updateRouteSubject(Long id, RouteSubjectCreateVo routeSubjectCreateVo) {
        String subjectName = routeSubjectCreateVo.getSubjectName();
        if(StringUtils.isBlank(subjectName)){
            return;
        }
        Optional<RouteSubject> routeSubjectOptional = routeSubjectRepository.findById(id);
        if(routeSubjectOptional.isEmpty()){
            return;
        }
        RouteSubject routeSubject = routeSubjectOptional.get();
        routeSubject.setSubjectName(subjectName);
        routeSubjectRepository.save(routeSubject);
    }

    @Override
    public BasePageRespVo<RouteSubjectListVo> listRouteSubject(RouteSubjectQueryVo routeSubjectQueryVo, Integer pageNum, Integer pageSize) {
        RouteSubject routeSubject = RouteConverter.INSTANCE.convertQueryVo2RouteSubjectEntity(routeSubjectQueryVo);
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        Page<RouteSubject> routeSubjectPage = routeSubjectRepository.findAll(Example.of(routeSubject, ExampleMatcher.matching().withIgnoreNullValues().withMatcher("subjectName", ExampleMatcher.GenericPropertyMatcher::contains)), PageRequest.of(pageNum - 1, size));
        List<RouteSubjectListVo> routeSubjectListVos = routeSubjectPage.getContent().stream().map(RouteConverter.INSTANCE::convertRouteSubjectEntity2ListVo).toList();
        return new BasePageRespVo<>(routeSubjectListVos,pageNum,size,routeSubjectPage.getTotalElements(), routeSubjectPage.getTotalPages());
    }

    @Override
    public List<RouteSubjectListVo> listAllRouteSubject() {
        Iterable<RouteSubject> all = routeSubjectRepository.findAll();
        return StreamSupport.stream(all.spliterator(), false).map(RouteConverter.INSTANCE::convertRouteSubjectEntity2ListVo).toList();
    }

    @Override
    public Long createRouteTemplate(RouteTemplateCreateVo routeTemplateCreateVo) {
        RouteTemplate routeTemplate = RouteConverter.INSTANCE.convertCreateVo2RouteTemplateEntity(routeTemplateCreateVo);
        RouteTemplate save = routeTemplateRepository.save(routeTemplate);
        return save.getId();
    }

    @Override
    public void updateRouteTemplate(Long id, RouteTemplateCreateVo routeTemplateCreateVo) {
        String templateName = routeTemplateCreateVo.getTemplateName();
        if(StringUtils.isBlank(templateName)){
            return;
        }
        Optional<RouteTemplate> routeTemplateOptional = routeTemplateRepository.findById(id);
        if(routeTemplateOptional.isEmpty()){
            return;
        }
        RouteTemplate routeTemplate = routeTemplateOptional.get();
        routeTemplate.setTemplateName(templateName);
        routeTemplate.setTemplateDays(routeTemplateCreateVo.getTemplateDays());
        routeTemplateRepository.save(routeTemplate);
    }

    @Override
    public List<String> listRouteDestination() {
        return routeInfoRepository.selectDestinationList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long transferRoute(RouteTransferVo routeTransferVo) {
        Long routeId = routeTransferVo.routeId();
        if(routeId == null || !routeInfoRepository.existsById(routeId)) {
            return null;
        }
        RouteContact routeContact = RouteConverter.INSTANCE.convertTransferVo2ContactEntity(routeTransferVo);
        RouteContact save = routeContactRepository.save(routeContact);
        increaseForwardNumber(routeId);
        return save.getId();
    }

    private void increaseForwardNumber(Long routeId){
        Long id = routeStatisticsRepository.findIdByRouteId(routeId);
        if(id == null){
            RouteStatistics routeStatistics = new RouteStatistics();
            routeStatistics.setRouteId(routeId);
            routeStatistics.setViewNumber(0);
            routeStatistics.setForward_number(1);
            routeStatisticsRepository.save(routeStatistics);
        }else {
            routeStatisticsRepository.increaseForwardNumber(routeId);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long batchTransferRoute(RouteBatchTransferVo routeBatchTransferVo) {
        List<Long> routeIds = routeBatchTransferVo.routeIds();
        RouteContact routeContact = RouteConverter.INSTANCE.convertBatchTransferVo2ContactEntity(routeBatchTransferVo);
        RouteContact save = routeContactRepository.save(routeContact);
        Long contactId = save.getId();
        if(CollectionUtils.isNotEmpty(routeIds)){
            routeIds.parallelStream().forEach(this::increaseForwardNumber);
            List<RefContactRoute> refContactRoutes = routeIds.stream().map(routeId -> {
                RefContactRoute refContactRoute = new RefContactRoute();
                refContactRoute.setRouteId(routeId);
                refContactRoute.setContactId(contactId);
                return refContactRoute;
            }).toList();
            refContactRouteRepository.saveAll(refContactRoutes);
        }
        return contactId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void viewRoute(Long routeId) {
        if(routeId == null || !routeInfoRepository.existsById(routeId)){
            return;
        }
        Long id = routeStatisticsRepository.findIdByRouteId(routeId);
        if(id == null){
            RouteStatistics routeStatistics = new RouteStatistics();
            routeStatistics.setRouteId(routeId);
            routeStatistics.setViewNumber(1);
            routeStatistics.setForward_number(0);
            routeStatisticsRepository.save(routeStatistics);
        }else {
            routeStatisticsRepository.increaseViewNumber(routeId);
        }
    }

    @Override
    public MyRouteListVo myRouteList(Long userId,RouteQueryVo routeQueryVo, Long contactId, String posterNo,Integer pageNum,Integer pageSize) {
        MyRouteListVo myRouteListVo = new MyRouteListVo();
        if(StringUtils.isNotBlank(posterNo)){
            Optional<Poster> posterOptional = posterRepository.findByPosterNo(posterNo);
            posterOptional.ifPresent(poster -> {
                PosterDetailVo posterDetailVo = PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
                myRouteListVo.setPosterInfo(posterDetailVo);
            });
        }
        if(contactId == null || contactId == 0L){
            //登录人创建的路线列表
            BasePageRespVo<RouteListVo> listRoute = listRoute(userId, routeQueryVo, pageNum, pageSize);
            myRouteListVo.setList(listRoute);
            myRouteListVo.setOriginalCreator(userId);
            RouteListVo routeListVo = listRoute.list().get(0);
            RouteContactVo routeContactVo = new RouteContactVo();
            routeContactVo.setContactUserId(userId);
            routeContactVo.setContactUserMobile(routeListVo.getRouteDesignerMobile());
            routeContactVo.setContactUserName(routeListVo.getRouteDesignerName());
            routeContactVo.setContactUserWxUri(routeListVo.getRouteDesignerWxUri());
            routeContactVo.setRouteTemplate(routeQueryVo.getRouteTemplate());
            myRouteListVo.setContactInfo(routeContactVo);
            return myRouteListVo;
        }

        Optional<RouteContact> contactOptional = routeContactRepository.findById(contactId);
        if(contactOptional.isPresent()){
            RouteContact routeContact = contactOptional.get();
            myRouteListVo.setContactInfo(RouteConverter.INSTANCE.convertContactEntity2Vo(routeContact));
//            Long total = routeInfoRepository.countByContactId(contactId, null);
//            pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
//            int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
//            BasePageRespVo<RouteListVo> basePageRespVo;
//            if(total > 0L){
//                List<RouteInfo> routeInfos = routeInfoRepository.selectPageByContactId(contactId,null,pageNum -1 ,size);
//                List<RouteListVo> routeListVos = routeInfos.stream().map(RouteConverter.INSTANCE::convertInfoEntity2ListVo).toList();
//                Long page = (total + size -1) /size;// total % size == 0 ? total / size : (total / size) + 1;
//                basePageRespVo = new BasePageRespVo<>(routeListVos,pageNum,size,total, page.intValue());
//            }else {
//                basePageRespVo = new BasePageRespVo<>(new ArrayList<>(0),pageNum,pageSize,0L,1);
//            }
            Long originalCreator = routeContact.getOriginalCreator();
            Long routeTemplate = routeContact.getRouteTemplate();
            routeQueryVo.setRouteTemplate(routeTemplate);
            BasePageRespVo<RouteListVo> listRoute = listRoute(originalCreator, routeQueryVo, pageNum, pageSize);
            myRouteListVo.setList(listRoute);
            myRouteListVo.setOriginalCreator(originalCreator);
        }
        return myRouteListVo;
    }

    @Override
    public List<RouteTemplateListVo> listAllRouteTemplate() {
        Iterable<RouteTemplate> all = routeTemplateRepository.findAll();
        return StreamSupport.stream(all.spliterator(), false).map(RouteConverter.INSTANCE::convertRouteTemplateEntity2ListVo).toList();
    }

    @Override
    public BasePageRespVo<RouteTemplateListVo> listRouteTemplate(RouteTemplateQueryVo routeTemplateQueryVo, Integer pageNum, Integer pageSize) {
        return listRouteTemplate(null,routeTemplateQueryVo,pageNum,pageSize);
    }

    @Override
    public BasePageRespVo<RouteTemplateListVo> listRouteTemplate(Long createUser, RouteTemplateQueryVo routeTemplateQueryVo, Integer pageNum, Integer pageSize) {
        RouteTemplate routeTemplate = RouteConverter.INSTANCE.convertQueryVo2RouteTemplateEntity(routeTemplateQueryVo);
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        Page<RouteTemplate> routeTemplatePage = routeTemplateRepository.findAll(Example.of(routeTemplate, ExampleMatcher.matching().withIgnoreNullValues().withMatcher("templateName", ExampleMatcher.GenericPropertyMatcher::contains)), PageRequest.of(pageNum - 1, size));
        List<RouteTemplate> content = routeTemplatePage.getContent();
        Set<Long> templateIds = content.stream().map(RouteTemplate::getId).collect(Collectors.toSet());
        List<RouteTemplateCountVo> routeTemplateCountVos = createUser == null ? routeTemplateRepository.selectRouteCountByTemplateIn(templateIds) :routeTemplateRepository.selectRouteCountByTemplateInAndCreateUser(templateIds,createUser);
        Map<Long, RouteTemplateCountVo> templateCountVoMap = routeTemplateCountVos.stream().collect(Collectors.toMap(RouteTemplateCountVo::getRouteTemplate, r -> r));
        List<RouteTemplateListVo> routeTemplateListVos = content.stream().map(rt -> {
            RouteTemplateListVo routeTemplateListVo = RouteConverter.INSTANCE.convertRouteTemplateEntity2ListVo(rt);
            Long id = rt.getId();
            RouteTemplateCountVo routeTemplateCountVo = templateCountVoMap.getOrDefault(id,new RouteTemplateCountVo(id,0L,0L));
            if(routeTemplateCountVo != null){
                routeTemplateListVo.setRouteCount(routeTemplateCountVo.getRouteCount());
                routeTemplateListVo.setSubjectCount(routeTemplateCountVo.getSubjectCount());
            }
            String encode = URLEncoder.encode("https://www.baidu.com", StandardCharsets.UTF_8);
            routeTemplateListVo.setQrUri("/travel/manage/photo/qrCode/generate?content="+encode);
            return routeTemplateListVo;
        }).toList();

        return new BasePageRespVo<>(routeTemplateListVos,pageNum,size,routeTemplatePage.getTotalElements(), routeTemplatePage.getTotalPages());
    }

    @Autowired
    public void setRouteInfoRepository(RouteInfoRepository routeInfoRepository) {
        this.routeInfoRepository = routeInfoRepository;
    }

    @Autowired
    public void setRouteIntroductionRepository(RouteIntroductionRepository routeIntroductionRepository) {
        this.routeIntroductionRepository = routeIntroductionRepository;
    }

    @Autowired
    public void setRouteScheduleRepository(RouteScheduleRepository routeScheduleRepository) {
        this.routeScheduleRepository = routeScheduleRepository;
    }

    @Autowired
    public void setRouteExtraInfoRepository(RouteExtraInfoRepository routeExtraInfoRepository) {
        this.routeExtraInfoRepository = routeExtraInfoRepository;
    }

    @Autowired
    public void setRouteBindPosterRepository(RouteBindPosterRepository routeBindPosterRepository) {
        this.routeBindPosterRepository = routeBindPosterRepository;
    }

    @Autowired
    public void setRouteTypeRepository(RouteTypeRepository routeTypeRepository) {
        this.routeTypeRepository = routeTypeRepository;
    }

    @Autowired
    public void setRouteSubjectRepository(RouteSubjectRepository routeSubjectRepository) {
        this.routeSubjectRepository = routeSubjectRepository;
    }

    @Autowired
    public void setRouteDetailInfoVoRepository(RouteDetailInfoVoRepository routeDetailInfoVoRepository) {
        this.routeDetailInfoVoRepository = routeDetailInfoVoRepository;
    }

    @Autowired
    public void setRouteTemplateRepository(RouteTemplateRepository routeTemplateRepository) {
        this.routeTemplateRepository = routeTemplateRepository;
    }

    @Autowired
    public void setRouteContactRepository(RouteContactRepository routeContactRepository) {
        this.routeContactRepository = routeContactRepository;
    }

    @Autowired
    public void setUserAccountRepository(UserAccountRepository userAccountRepository) {
        this.userAccountRepository = userAccountRepository;
    }

    @Autowired
    public void setRouteStatisticsRepository(RouteStatisticsRepository routeStatisticsRepository) {
        this.routeStatisticsRepository = routeStatisticsRepository;
    }

    @Autowired
    public void setPosterRepository(PosterRepository posterRepository) {
        this.posterRepository = posterRepository;
    }

    @Autowired
    public void setRefContactRouteRepository(RefContactRouteRepository refContactRouteRepository) {
        this.refContactRouteRepository = refContactRouteRepository;
    }
}
